using System;
using System.Collections.Generic;
using System.Text;
using System.Collections.ObjectModel;
using System.ComponentModel;
using AntEater.ComponentModel.BuildElements.Design;

namespace AntEater.ComponentModel.BuildElements
{
    /// <summary>
    /// Provides a collection of targets.
    /// </summary>
    [Serializable()]
    [TypeConverter(typeof(TargetCollectionConverter))]
    public class TargetCollection:Collection<string>, ICloneable
    {
        /// <summary>
        /// Occurs when the content of the collection changed
        /// </summary>
        public event EventHandler<CollectionChangeEventArgs> CollectionChanged;

        /// <summary>
        /// Initializes a new instance of the <see cref="T:TargetCollection"/> class.
        /// </summary>
        public TargetCollection(BuildFileDefinition context)
            : base() {
            this._context = context;
            this._context.TargetNameChangeTracker.TrackedPropertyChanged += new EventHandler<TrackedPropertyChangedEventArgs>(TargetNameChanged);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="T:TargetCollection"/> class.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="list">The list that is wrapped by the new collection.</param>
        /// <exception cref="T:System.ArgumentNullException">list is null.</exception>
        public TargetCollection(BuildFileDefinition context, IList<string> list)
            : base(list) {
            this._context = context;
        }

        private BuildFileDefinition _context;

        /// <summary>
        /// Gets or sets the context.
        /// </summary>
        /// <value>The context.</value>
        public BuildFileDefinition Context {
            get { return _context; }
            set { _context = value; }
        }

        /// <summary>
        /// Adds the range.
        /// </summary>
        /// <param name="collection">The collection.</param>
        public void AddRange(IEnumerable<string> collection) {
            foreach (string item in collection) {
                this.Add(item);
            }
        }

        /// <summary>
        /// Returns the array.
        /// </summary>
        /// <returns></returns>
        public string[] ToArray() {
            string[] result = new string[this.Count];

            this.CopyTo(result, 0);

            return result;
        }

        /// <summary>
        /// Returns a <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
        /// </returns>
        public override string ToString() {
            return string.Join(";", this.ToArray());
        }

        /// <summary>
        /// Creates a new object that is a copy of the current instance.
        /// </summary>
        /// <returns>
        /// A new object that is a copy of this instance.
        /// </returns>
        public object Clone() {
            TargetCollection newCollection = new TargetCollection(this.Context);

            foreach (string target in this) {
                newCollection.Add(target.Clone() as string);
            }

            return newCollection;
        }

        /// <summary>
        /// Inserts an element into the <see cref="T:System.Collections.ObjectModel.Collection`1"></see> at the specified index.
        /// </summary>
        /// <param name="index">The zero-based index at which item should be inserted.</param>
        /// <param name="item">The object to insert. The value can be null for reference types.</param>
        /// <exception cref="T:System.ArgumentOutOfRangeException">index is less than zero.-or-index is greater than <see cref="P:System.Collections.ObjectModel.Collection`1.Count"></see>.</exception>
        protected override void InsertItem(int index, string item) {
            base.InsertItem(index, item);
            OnCollectionChanged(CollectionChangeAction.Add);
        }

        /// <summary>
        /// Replaces the element at the specified index.
        /// </summary>
        /// <param name="index">The zero-based index of the element to replace.</param>
        /// <param name="item">The new value for the element at the specified index. The value can be null for reference types.</param>
        /// <exception cref="T:System.ArgumentOutOfRangeException">index is less than zero.-or-index is greater than <see cref="P:System.Collections.ObjectModel.Collection`1.Count"></see>.</exception>
        protected override void SetItem(int index, string item) {
            base.SetItem(index, item);
            OnCollectionChanged(CollectionChangeAction.Refresh);
        }

        /// <summary>
        /// Removes the element at the specified index of the <see cref="T:System.Collections.ObjectModel.Collection`1"></see>.
        /// </summary>
        /// <param name="index">The zero-based index of the element to remove.</param>
        /// <exception cref="T:System.ArgumentOutOfRangeException">index is less than zero.-or-index is equal to or greater than <see cref="P:System.Collections.ObjectModel.Collection`1.Count"></see>.</exception>
        protected override void RemoveItem(int index) {
            base.RemoveItem(index);
            OnCollectionChanged(CollectionChangeAction.Remove);
        }

        /// <summary>
        /// Removes all elements from the <see cref="T:System.Collections.ObjectModel.Collection`1"></see>.
        /// </summary>
        protected override void ClearItems() {
            base.ClearItems();
            OnCollectionChanged(CollectionChangeAction.Refresh);
        }

        /// <summary>
        /// Called when [collection changed].
        /// </summary>
        /// <param name="action">The action.</param>
        protected void OnCollectionChanged(CollectionChangeAction action) {
            if (CollectionChanged != null) {
                CollectionChanged(this, new CollectionChangeEventArgs(action, this));
            }
        }

        /// <summary>
        /// Targets the name changed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="T:AntEater.ComponentModel.TrackedPropertyChangedEventArgs"/> instance containing the event data.</param>
        private void TargetNameChanged(object sender, TrackedPropertyChangedEventArgs e) {
            string oldTargetName = e.OldPropertyValue as string;
            string newTargetName = e.NewPropertyValue as string;

            if (this.Items.Contains(oldTargetName)) {
                this.Items[this.Items.IndexOf(oldTargetName)] = newTargetName;
            }
        }
    }
}
